Animation System
**Referenced Files in This Document** - [main.js](file://src/assets/js/main.js) - [hero-animations.js](file://src/assets/js/modules/hero-animations.js) - [hero-parallax.js](file://src/assets/js/modules/hero-parallax.js) - [material-design-3-main-theme-toggle.js](file://src/assets/js/modules/material-design-3-main-theme-toggle.js) - [iaa-alliance-theme-toggle.js](file://src/assets/js/modules/iaa-alliance-theme-toggle.js) - [theme-toggling.js](file://src/assets/js/modules/theme-toggling.js) - [poll-bar-animation.js](file://src/assets/js/modules/poll-bar-animation.js) - [38-material-design-3-theme-toggle.css](file://src/assets/css/modules/38-material-design-3-theme-toggle.css) - [39-material-design-3-theme-toggle-complete.css](file://src/assets/css/modules/39-material-design-3-theme-toggle-complete.css) - [40-iaa-alliance-page-light-dark-mode.css](file://src/assets/css/modules/40-iaa-alliance-page-light-dark-mode.css)Table of Contents
- Introduction
- Project Structure
- Core Components
- Architecture Overview
- Detailed Component Analysis
- Dependency Analysis
- Performance Considerations
- Troubleshooting Guide
- Conclusion
- Appendices
Introduction
This document describes the animation system built with GSAP and ScrollTrigger, focusing on:
- Hero animations with complex 3D parallax and scroll-driven terrain effects
- Material Design 3 theme toggles for smooth theme transitions
- Alliance-specific theme switching animations
- Practical examples of GSAP timelines, ScrollTrigger configuration, and animation sequencing
- Performance optimization, browser compatibility, fallbacks, and debugging techniques
Project Structure
The animation system is organized around modular JavaScript initialization functions and complementary CSS theme styles. The main entry wires all modules together and conditionally registers ScrollTrigger when GSAP is available.
graph TB
Main["main.js<br/>Entry point and module wiring"] --> GA["hero-animations.js<br/>GSAP timelines + ScrollTrigger"]
Main --> GP["hero-parallax.js<br/>Three.js 3D parallax scene"]
Main --> MT["material-design-3-main-theme-toggle.js<br/>Main theme toggle"]
Main --> IAAT["iaa-alliance-theme-toggle.js<br/>Alliance theme toggle"]
Main --> TT["theme-toggling.js<br/>Section theme observer"]
Main --> PB["poll-bar-animation.js<br/>IntersectionObserver reveal"]
GA --> CSS1["38-material-design-3-theme-toggle.css"]
GA --> CSS2["39-material-design-3-theme-toggle-complete.css"]
IAAT --> CSS3["40-iaa-alliance-page-light-dark-mode.css"]
Diagram sources
- [main.js:15-36](file://src/assets/js/main.js#L15-L36)
- [hero-animations.js:3-44](file://src/assets/js/modules/hero-animations.js#L3-L44)
- [hero-parallax.js:368-446](file://src/assets/js/modules/hero-parallax.js#L368-L446)
- [material-design-3-main-theme-toggle.js:3-35](file://src/assets/js/modules/material-design-3-main-theme-toggle.js#L3-L35)
- [iaa-alliance-theme-toggle.js:3-35](file://src/assets/js/modules/iaa-alliance-theme-toggle.js#L3-L35)
- [theme-toggling.js:3-21](file://src/assets/js/modules/theme-toggling.js#L3-L21)
- [poll-bar-animation.js:3-17](file://src/assets/js/modules/poll-bar-animation.js#L3-L17)
- [38-material-design-3-theme-toggle.css:16-185](file://src/assets/css/modules/38-material-design-3-theme-toggle.css#L16-L185)
- [39-material-design-3-theme-toggle-complete.css:16-653](file://src/assets/css/modules/39-material-design-3-theme-toggle-complete.css#L16-L653)
- [40-iaa-alliance-page-light-dark-mode.css:212-468](file://src/assets/css/modules/40-iaa-alliance-page-light-dark-mode.css#L212-L468)
Section sources
- [main.js:15-36](file://src/assets/js/main.js#L15-L36)
Core Components
- GSAP-based hero animations with staggered reveals, SVG path drawing, waypoint triggers, and a split-flap headline effect
- Scroll-driven parallax with Three.js terrain, camera movement, and particle trails
- Material Design 3 theme toggles with persistent preferences and keyboard accessibility
- Section-based theme switching via IntersectionObserver
- Poll bar animations using IntersectionObserver for scroll-triggered width reveals
Section sources
- [hero-animations.js:3-44](file://src/assets/js/modules/hero-animations.js#L3-L44)
- [hero-parallax.js:368-446](file://src/assets/js/modules/hero-parallax.js#L368-L446)
- [material-design-3-main-theme-toggle.js:3-35](file://src/assets/js/modules/material-design-3-main-theme-toggle.js#L3-L35)
- [iaa-alliance-theme-toggle.js:3-35](file://src/assets/js/modules/iaa-alliance-theme-toggle.js#L3-L35)
- [theme-toggling.js:3-21](file://src/assets/js/modules/theme-toggling.js#L3-L21)
- [poll-bar-animation.js:3-17](file://src/assets/js/modules/poll-bar-animation.js#L3-L17)
Architecture Overview
The system integrates GSAP timelines and ScrollTrigger for scroll-driven animations, while Three.js powers a dedicated parallax scene. Theme toggles are implemented with local storage and CSS custom properties for immediate visual feedback.
sequenceDiagram
participant DOM as "DOMContentLoaded"
participant Main as "main.js"
participant GSAP as "GSAP + ScrollTrigger"
participant Hero as "hero-animations.js"
participant Parallax as "hero-parallax.js"
participant Themes as "theme-toggles"
DOM->>Main : Initialize modules
Main->>GSAP : Register ScrollTrigger if available
Main->>Hero : initHeroAnimations()
Hero->>GSAP : Create timelines, set reduced motion fallbacks
Hero->>Parallax : initHeroParticles() (canvas)
Hero->>GSAP : ScrollTrigger.create() for parallax
Main->>Parallax : initHeroParallax() (Three.js scene)
Parallax->>Parallax : animate() loop with requestAnimationFrame
Main->>Themes : initThemeToggle(), initIAAThemeToggle(), initThemeToggling()
Diagram sources
- [main.js:15-36](file://src/assets/js/main.js#L15-L36)
- [hero-animations.js:3-44](file://src/assets/js/modules/hero-animations.js#L3-L44)
- [hero-parallax.js:368-446](file://src/assets/js/modules/hero-parallax.js#L368-L446)
- [material-design-3-main-theme-toggle.js:3-35](file://src/assets/js/modules/material-design-3-main-theme-toggle.js#L3-L35)
- [iaa-alliance-theme-toggle.js:3-35](file://src/assets/js/modules/iaa-alliance-theme-toggle.js#L3-L35)
- [theme-toggling.js:3-21](file://src/assets/js/modules/theme-toggling.js#L3-L21)
Detailed Component Analysis
Hero Animations (GSAP + ScrollTrigger)
Key behaviors:
- Detects a dedicated parallax hero and bypasses standard hero animations to avoid conflicts
- Applies reduced motion fallbacks using prefers-reduced-motion
- Initializes particle background, Southern Cross twinkling, topo line drawing, and navigation path reveal
- Uses ScrollTrigger to drive parallax depth during scroll
- Implements a split-flap headline reveal synchronized with subtitle/CTA
flowchart TD
Start(["initHeroAnimations"]) --> CheckParallax{"Parallax hero present?"}
CheckParallax --> |Yes| Exit["Return (handled by parallax module)"]
CheckParallax --> |No| Reduced{"prefers-reduced-motion?"}
Reduced --> |Yes| ReduceTL["Reduced motion timeline"]
Reduced --> |No| InitParts["initHeroParticles()"]
InitParts --> InitCross["initSouthernCross()"]
InitCross --> InitTopo["initTopoLines()"]
InitTopo --> InitNav["initNavPath()"]
InitNav --> Flipboard["initHeroFlipboard()"]
Flipboard --> Reveal["initScrollReveals()"]
ReduceTL --> Reveal
Diagram sources
- [hero-animations.js:3-44](file://src/assets/js/modules/hero-animations.js#L3-L44)
- [hero-animations.js:48-103](file://src/assets/js/modules/hero-animations.js#L48-L103)
- [hero-animations.js:107-121](file://src/assets/js/modules/hero-animations.js#L107-L121)
- [hero-animations.js:125-152](file://src/assets/js/modules/hero-animations.js#L125-L152)
- [hero-animations.js:156-209](file://src/assets/js/modules/hero-animations.js#L156-L209)
- [hero-animations.js:213-276](file://src/assets/js/modules/hero-animations.js#L213-L276)
- [hero-animations.js:283-304](file://src/assets/js/modules/hero-animations.js#L283-L304)
Practical examples:
- GSAP timeline creation and defaults: [hero-animations.js:33-42](file://src/assets/js/modules/hero-animations.js#L33-L42)
- ScrollTrigger configuration for parallax: [hero-animations.js:142-151](file://src/assets/js/modules/hero-animations.js#L142-L151)
- Staggered SVG stroke-dashoffset animation: [hero-animations.js:134-139](file://src/assets/js/modules/hero-animations.js#L134-L139)
- Waypoint timeline sequencing: [hero-animations.js:194-198](file://src/assets/js/modules/hero-animations.js#L194-L198)
- Split-flap character flipping: [hero-animations.js:250-270](file://src/assets/js/modules/hero-animations.js#L250-L270)
Section sources
- [hero-animations.js:3-44](file://src/assets/js/modules/hero-animations.js#L3-L44)
- [hero-animations.js:48-103](file://src/assets/js/modules/hero-animations.js#L48-L103)
- [hero-animations.js:107-121](file://src/assets/js/modules/hero-animations.js#L107-L121)
- [hero-animations.js:125-152](file://src/assets/js/modules/hero-animations.js#L125-L152)
- [hero-animations.js:156-209](file://src/assets/js/modules/hero-animations.js#L156-L209)
- [hero-animations.js:213-276](file://src/assets/js/modules/hero-animations.js#L213-L276)
- [hero-animations.js:283-304](file://src/assets/js/modules/hero-animations.js#L283-L304)
Scroll-Driven Terrain Parallax (Three.js + GSAP)
Key behaviors:
- Manages its own Three.js animation loop with requestAnimationFrame and sleep/wake logic
- Builds terrain contours incrementally to avoid blocking the main thread
- Smooths scroll progress and drives camera along a parametric path
- Updates sky layer translation and fades the 3D scene as content scrolls into view
- Applies theme-dependent materials and textures
sequenceDiagram
participant Window as "Window"
participant Scene as "Three.js Scene"
participant Loop as "animate()"
participant Camera as "Camera"
participant Trail as "Trail & Sparks"
Window->>Loop : requestAnimationFrame
Loop->>Loop : readScroll() -> smoothProgress
Loop->>Scene : compute node/camera positions
Loop->>Camera : set position + lookAt
Loop->>Trail : appendTrailPoint(), updateTrailFade(), updateSparks()
Loop->>Scene : skyLayer.translateX, updateText(progress)
Loop->>Scene : fadeOut = f(progress)
Loop->>Scene : renderer.render()
alt Scene faded out and motion settled
Loop->>Loop : sleeping = true
else Continue animating
Loop->>Loop : requestAnimationFrame(animate)
end
Diagram sources
- [hero-parallax.js:298-306](file://src/assets/js/modules/hero-parallax.js#L298-L306)
- [hero-parallax.js:392-446](file://src/assets/js/modules/hero-parallax.js#L392-L446)
- [hero-parallax.js:449-462](file://src/assets/js/modules/hero-parallax.js#L449-L462)
Practical examples:
- Scroll smoothing and camera movement: [hero-parallax.js:399-411](file://src/assets/js/modules/hero-parallax.js#L399-L411)
- Sky layer translation and fade: [hero-parallax.js:426-433](file://src/assets/js/modules/hero-parallax.js#L426-L433)
- Theme-aware material updates: [hero-parallax.js:449-462](file://src/assets/js/modules/hero-parallax.js#L449-L462)
Section sources
- [hero-parallax.js:298-306](file://src/assets/js/modules/hero-parallax.js#L298-L306)
- [hero-parallax.js:392-446](file://src/assets/js/modules/hero-parallax.js#L392-L446)
- [hero-parallax.js:449-462](file://src/assets/js/modules/hero-parallax.js#L449-L462)
Material Design 3 Theme Toggle (Main Site)
Key behaviors:
- Reads and applies a saved theme preference from local storage
- Toggles CSS classes on the body to switch theme variants
- Updates ARIA state for accessibility
- Supports keyboard activation (Enter/Space)
flowchart TD
Init(["initThemeToggle"]) --> LoadPref["Load saved theme from localStorage"]
LoadPref --> Apply["Apply theme classes and aria-checked"]
Apply --> Click["User clicks toggle"]
Click --> Switch["Switch theme class and update aria-checked"]
Switch --> Persist["Save new theme to localStorage"]
Click --> Keydown["User presses Enter/Space"]
Keydown --> Click
Diagram sources
- [material-design-3-main-theme-toggle.js:3-35](file://src/assets/js/modules/material-design-3-main-theme-toggle.js#L3-L35)
Practical examples:
- Local storage persistence: [material-design-3-main-theme-toggle.js:10-26](file://src/assets/js/modules/material-design-3-main-theme-toggle.js#L10-L26)
- Accessibility attributes: [material-design-3-main-theme-toggle.js:15-24](file://src/assets/js/modules/material-design-3-main-theme-toggle.js#L15-L24)
Section sources
- [material-design-3-main-theme-toggle.js:3-35](file://src/assets/js/modules/material-design-3-main-theme-toggle.js#L3-L35)
- [38-material-design-3-theme-toggle.css:16-185](file://src/assets/css/modules/38-material-design-3-theme-toggle.css#L16-L185)
- [39-material-design-3-theme-toggle-complete.css:16-653](file://src/assets/css/modules/39-material-design-3-theme-toggle-complete.css#L16-L653)
IAA Alliance Theme Toggle
Key behaviors:
- Standalone theme toggle for the alliance page with its own storage key
- Mirrors the main theme toggle pattern with distinct selectors and CSS hooks
flowchart TD
Init(["initIAAThemeToggle"]) --> LoadPref["Load saved theme from localStorage"]
LoadPref --> Apply["Apply theme classes and aria-checked"]
Apply --> Click["User clicks toggle"]
Click --> Switch["Switch theme class and update aria-checked"]
Switch --> Persist["Save new theme to localStorage"]
Click --> Keydown["User presses Enter/Space"]
Keydown --> Click
Diagram sources
- [iaa-alliance-theme-toggle.js:3-35](file://src/assets/js/modules/iaa-alliance-theme-toggle.js#L3-L35)
Practical examples:
- Local storage persistence: [iaa-alliance-theme-toggle.js:10-26](file://src/assets/js/modules/iaa-alliance-theme-toggle.js#L10-L26)
- CSS integration for alliance page: [40-iaa-alliance-page-light-dark-mode.css:426-468](file://src/assets/css/modules/40-iaa-alliance-page-light-dark-mode.css#L426-L468)
Section sources
- [iaa-alliance-theme-toggle.js:3-35](file://src/assets/js/modules/iaa-alliance-theme-toggle.js#L3-L35)
- [40-iaa-alliance-page-light-dark-mode.css:426-468](file://src/assets/css/modules/40-iaa-alliance-page-light-dark-mode.css#L426-L468)
Section-Based Theme Switching
Key behaviors:
- Observes sections with a data-theme attribute
- Toggles global body classes as sections enter the viewport
- Uses a centered viewport intersection to reliably switch themes
sequenceDiagram
participant Sections as "[data-theme] sections"
participant IO as "IntersectionObserver"
participant Body as "document.body"
Sections->>IO : Observe sections
IO->>Sections : on intersect
Sections->>Body : toggle 'on-light-bg'/'on-dark-bg'
Diagram sources
- [theme-toggling.js:3-21](file://src/assets/js/modules/theme-toggling.js#L3-L21)
Section sources
- [theme-toggling.js:3-21](file://src/assets/js/modules/theme-toggling.js#L3-L21)
Poll Bar Animation
Key behaviors:
- Uses IntersectionObserver to animate bar widths when elements become visible
- Unobserves elements after animation to prevent repeated work
flowchart TD
Start(["initPollAnimation"]) --> Observe["Observe .poll-bar-segment[data-value]"]
Observe --> Visible{"Element intersects?"}
Visible --> |Yes| Animate["Set width to dataset value"]
Animate --> Unobserve["Unobserve element"]
Visible --> |No| Wait["Wait for intersection"]
Diagram sources
- [poll-bar-animation.js:3-17](file://src/assets/js/modules/poll-bar-animation.js#L3-L17)
Section sources
- [poll-bar-animation.js:3-17](file://src/assets/js/modules/poll-bar-animation.js#L3-L17)
Dependency Analysis
- main.js orchestrates initialization and conditionally registers ScrollTrigger
- hero-animations.js depends on GSAP and ScrollTrigger for timelines and scroll-driven effects
- hero-parallax.js runs independently with Three.js and its own animation loop
- Theme toggle modules depend on CSS custom properties and local storage
- theme-toggling.js coordinates theme state changes across sections
graph LR
Main["main.js"] --> GA["hero-animations.js"]
Main --> MT["material-design-3-main-theme-toggle.js"]
Main --> IAAT["iaa-alliance-theme-toggle.js"]
Main --> TT["theme-toggling.js"]
GA --> ST["ScrollTrigger (registered in main.js)"]
GA --> CSS1["38-material-design-3-theme-toggle.css"]
GA --> CSS2["39-material-design-3-theme-toggle-complete.css"]
IAAT --> CSS3["40-iaa-alliance-page-light-dark-mode.css"]
Diagram sources
- [main.js:15-36](file://src/assets/js/main.js#L15-L36)
- [hero-animations.js:3-44](file://src/assets/js/modules/hero-animations.js#L3-L44)
- [material-design-3-main-theme-toggle.js:3-35](file://src/assets/js/modules/material-design-3-main-theme-toggle.js#L3-L35)
- [iaa-alliance-theme-toggle.js:3-35](file://src/assets/js/modules/iaa-alliance-theme-toggle.js#L3-L35)
- [38-material-design-3-theme-toggle.css:16-185](file://src/assets/css/modules/38-material-design-3-theme-toggle.css#L16-L185)
- [39-material-design-3-theme-toggle-complete.css:16-653](file://src/assets/css/modules/39-material-design-3-theme-toggle-complete.css#L16-L653)
- [40-iaa-alliance-page-light-dark-mode.css:426-468](file://src/assets/css/modules/40-iaa-alliance-page-light-dark-mode.css#L426-L468)
Section sources
- [main.js:15-36](file://src/assets/js/main.js#L15-L36)
Performance Considerations
- Throttling and low-frequency updates:
- Scroll progress smoothing reduces jitter and stabilizes camera movement: [hero-parallax.js:399-411](file://src/assets/js/modules/hero-parallax.js#L399-L411)
- Scroll-driven parallax uses ScrollTrigger scrub for smooth interpolation: [hero-animations.js:142-151](file://src/assets/js/modules/hero-animations.js#L142-L151)
- GPU acceleration and compositing:
- Canvas-based particle rendering leverages GPU for line and point drawing: [hero-animations.js:67-103](file://src/assets/js/modules/hero-animations.js#L67-L103)
- Three.js scene uses additive blending and optimized buffer attributes: [hero-parallax.js:250-254](file://src/assets/js/modules/hero-parallax.js#L250-L254)
- Memory management:
- Trail buffers copyWithin to recycle arrays and avoid allocations: [hero-parallax.js:260-268](file://src/assets/js/modules/hero-parallax.js#L260-L268)
- Scene sleeps when faded out to minimize work: [hero-parallax.js:438-442](file://src/assets/js/modules/hero-parallax.js#L438-L442)
- Off-main-thread work:
- Chunked contour generation uses requestIdleCallback or setTimeout to spread work: [hero-parallax.js:89-91](file://src/assets/js/modules/hero-parallax.js#L89-L91)
- Reduced motion and accessibility:
- Prefers-reduced-motion fallbacks disable complex animations: [hero-animations.js:22-26](file://src/assets/js/modules/hero-animations.js#L22-L26)
- Event listener best practices:
- Passive listeners for scroll and resize to avoid layout thrashing: [hero-parallax.js:305](file://src/assets/js/modules/hero-parallax.js#L305), [hero-animations.js:100](file://src/assets/js/modules/hero-animations.js#L100)
[No sources needed since this section provides general guidance]
Troubleshooting Guide
Common issues and remedies:
- GSAP not loaded:
- The system checks for GSAP and logs a warning if missing; core features remain functional: [main.js:33-35](file://src/assets/js/main.js#L33-L35)
- ScrollTrigger not registered:
- Ensure ScrollTrigger is registered after GSAP loads; the entry point conditionally registers it: [main.js:28-30](file://src/assets/js/main.js#L28-L30)
- Hero animations not triggering:
- Verify the presence of the parallax hero selector to avoid double-initialization: [hero-animations.js:5](file://src/assets/js/modules/hero-animations.js#L5)
- Confirm reduced motion media query behavior and fallbacks: [hero-animations.js:22-26](file://src/assets/js/modules/hero-animations.js#L22-L26)
- Parallax scene not animating:
- Check visibility change and scroll wake-up logic: [hero-parallax.js:387-390](file://src/assets/js/modules/hero-parallax.js#L387-L390)
- Ensure the scene fades out properly to allow sleep: [hero-parallax.js:432-433](file://src/assets/js/modules/hero-parallax.js#L432-L433)
- Theme toggle not persisting:
- Confirm localStorage keys and that click/keydown handlers fire: [material-design-3-main-theme-toggle.js:10-26](file://src/assets/js/modules/material-design-3-main-theme-toggle.js#L10-L26), [iaa-alliance-theme-toggle.js:10-26](file://src/assets/js/modules/iaa-alliance-theme-toggle.js#L10-L26)
- Poll bars not animating:
- Verify IntersectionObserver thresholds and that unobserve occurs after animation: [poll-bar-animation.js:7-16](file://src/assets/js/modules/poll-bar-animation.js#L7-L16)
Section sources
- [main.js:28-35](file://src/assets/js/main.js#L28-L35)
- [hero-animations.js:5](file://src/assets/js/modules/hero-animations.js#L5)
- [hero-animations.js:22-26](file://src/assets/js/modules/hero-animations.js#L22-L26)
- [hero-parallax.js:387-390](file://src/assets/js/modules/hero-parallax.js#L387-L390)
- [hero-parallax.js:432-433](file://src/assets/js/modules/hero-parallax.js#L432-L433)
- [material-design-3-main-theme-toggle.js:10-26](file://src/assets/js/modules/material-design-3-main-theme-toggle.js#L10-L26)
- [iaa-alliance-theme-toggle.js:10-26](file://src/assets/js/modules/iaa-alliance-theme-toggle.js#L10-L26)
- [poll-bar-animation.js:7-16](file://src/assets/js/modules/poll-bar-animation.js#L7-L16)
Conclusion
The animation system combines GSAP timelines, ScrollTrigger-driven parallax, and a dedicated Three.js scene to deliver immersive hero experiences. Theme toggles adhere to Material Design 3 principles with robust persistence and accessibility. Performance is prioritized through smoothing, chunked work, and sleep/wake strategies, while reduced motion and IntersectionObserver-based reveals ensure graceful degradation.
[No sources needed since this section summarizes without analyzing specific files]
Appendices
Browser Compatibility and Fallbacks
- GSAP and ScrollTrigger availability guarded by feature detection in the entry point
- Reduced motion media query provides fallback animations
- IntersectionObserver is used for scroll reveals and poll bars, with feature checks
Section sources
- [main.js:28-35](file://src/assets/js/main.js#L28-L35)
- [hero-animations.js:8](file://src/assets/js/modules/hero-animations.js#L8)
- [hero-animations.js:283-304](file://src/assets/js/modules/hero-animations.js#L283-L304)
- [poll-bar-animation.js:7-16](file://src/assets/js/modules/poll-bar-animation.js#L7-L16)
Timing Functions and Easing Curves
- Hero timelines commonly use power-based easings for natural motion
- Scroll-driven parallax uses linear scrubbing for smoothness
- Split-flap uses rapid scaleY transitions with power-based easing for crispness
Section sources
- [hero-animations.js:33-42](file://src/assets/js/modules/hero-animations.js#L33-L42)
- [hero-animations.js:142-151](file://src/assets/js/modules/hero-animations.js#L142-L151)
- [hero-animations.js:256-264](file://src/assets/js/modules/hero-animations.js#L256-L264)
Responsive Animation Scaling
- Canvas particle density scales with viewport area
- Scroll thresholds and observer margins adapt to content density
Section sources
- [hero-animations.js:57](file://src/assets/js/modules/hero-animations.js#L57)
- [theme-toggling.js:18](file://src/assets/js/modules/theme-toggling.js#L18)